package com.sghj.config.handler.exceptionHandler;

import cn.hutool.core.util.StrUtil;
import com.sghj.config.ReturnResult;
import com.sghj.config.handler.ResultCode;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;


@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    //指定出现什么异常执行这个方法
    @ExceptionHandler({Exception.class})
    @ResponseBody
    public ReturnResult error(Exception e){
        //log.error(e.getMessage());
        //if(e instanceof  DuplicateKeyException){
        //    DuplicateKeyException ex = (DuplicateKeyException) e;
        //    return ReturnResult.failed().code(ResultCode.DUPLICATEKEY).message(ex.getMessage());
        //}
        log.error("meg",e);
        return ReturnResult.failed().message("发生错误").data("message",e.getMessage());
    }

    //自定义异常处理
    @ExceptionHandler(MyException.class)
    @ResponseBody
    public ReturnResult error(MyException e){
        //e.printStackTrace();
        log.info(e.getMessage());
        return ReturnResult.failed().code(e.getCode()).message(e.getMessage());
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseBody
    public ReturnResult error(MethodArgumentTypeMismatchException e){
        log.info("MethodArgumentTypeMismatchException:",e.getMessage());
        return ReturnResult.failed().code(ResultCode.PARAMETER).message("参数类型转换异常");
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ReturnResult error(MethodArgumentNotValidException e){
        log.warn("MethodArgumentNotValidException:",e.getMessage());
        return ReturnResult.failed().code(ResultCode.PARAMETER).message(e.getBindingResult().getFieldError().getDefaultMessage());
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ReturnResult error(ConstraintViolationException e){
        log.warn("ConstraintViolationException:",e.getMessage());
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        String message = violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(";"));

        return ReturnResult.failed().code(ResultCode.PARAMETER).message(message);
    }

    @ExceptionHandler(UnauthenticatedException.class)
    @ResponseBody
    public ReturnResult error(UnauthenticatedException e){
        log.info("UnauthenticatedException:",e.getMessage());
        return ReturnResult.failed().code(ResultCode.NEEDRELOGIN).message("身份信息已过期或账号在其他设备登录，请重新登录");
    }

    @ExceptionHandler(UnauthorizedException.class)
    @ResponseBody
    public ReturnResult error(UnauthorizedException e){
        log.error("UnauthorizedException:",e.getMessage());
        return ReturnResult.failed().code(ResultCode.PARAMETER).message("没有权限");
    }

    @ExceptionHandler(ExpiredJwtException.class)
    @ResponseBody
    public ReturnResult error(ExpiredJwtException e){
        log.info("ExpiredJwtException:",e.getMessage());
        return ReturnResult.failed().code(ResultCode.PARAMETER).message("登录信息过期，请重新登录");
    }

    @ExceptionHandler(MalformedJwtException.class)
    @ResponseBody
    public ReturnResult error(MalformedJwtException e){
        log.error("MalformedJwtException:",e.getMessage());
        return ReturnResult.failed().code(ResultCode.PARAMETER).message("token读取失败，请检查token是否正确");
    }


    @ExceptionHandler(BindException.class)
    @ResponseBody
    public ReturnResult bindException(BindException ex) {
        log.error("BindException:", ex);
        try {
            // 拿到@NotNull,@NotBlank和 @NotEmpty等注解上的message值
            String msg = Objects.requireNonNull(ex.getBindingResult().getFieldError()).getDefaultMessage();
            if (StrUtil.isNotEmpty(msg)) {
                // 自定义状态返回
                ReturnResult.failed().code(ResultCode.PARAMETER).message(msg);
            }
        } catch (Exception ignored) {
        }
        // 参数类型不匹配检验
        StringBuilder msg = new StringBuilder();
        List<FieldError> fieldErrors = ex.getFieldErrors();
        fieldErrors.forEach((oe) ->
                msg.append("参数:[").append(oe.getObjectName())
                        .append(".").append(oe.getField())
                        .append("]的传入值:[").append(oe.getRejectedValue()).append("]与预期的字段类型不匹配.")
        );
        return ReturnResult.failed().code(ResultCode.PARAMETER).message(msg.toString());
    }
}
